package ua.mykola.graph;

import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;

/**
 * Adjacency list graph implementation.
 */
public class AdjListGraph implements Graph {

	private final Map<Vertex, Set<Edge>> vertexToAdjList;
	private Set<Vertex> allVertices = new HashSet<Vertex>();

	public AdjListGraph() {
		vertexToAdjList = new LinkedHashMap<Vertex, Set<Edge>>();
	}

	public AdjListGraph(int numberOfVertices) {
		vertexToAdjList = new LinkedHashMap<Vertex, Set<Edge>>(numberOfVertices);
	}

	@Override
	public void addEdges(String sideA, String... sideB) {
		for (String vertex : sideB) {
			addEdges(new Edge(new Vertex(sideA), new Vertex(vertex)));
		}
	}

	@Override
	public void addEdges(Vertex sideA, Vertex... sideB) {
		for (Vertex vertex : sideB) {
			addEdges(new Edge(sideA, vertex));
		}
	}

	@Override
	public void addEdges(Edge... edges) {
		for (Edge edge : edges) {
			link(edge.getVertexA(), edge);
			link(edge.getVertexB(), edge);
		}
	}

	@Override
	public void addEdge(String sideA, String sideB, int weight) {
		addEdges(new Edge(new Vertex(sideA), new Vertex(sideB), weight));
	}

	private void link(final Vertex vertex1, Edge edge) {
		Set<Edge> set = vertexToAdjList.get(vertex1);
		if (set == null) {
			set = new TreeSet<Edge>(new OtherSideLabelComparator(vertex1));
			vertexToAdjList.put(vertex1, set);
		}
		set.add(edge);
		allVertices.add(vertex1);
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("Graph");
		for (Entry<Vertex, Set<Edge>> entry : vertexToAdjList.entrySet()) {
			sb.append("\n");
			sb.append(entry.getKey().getLabel() + ":");
			for (Edge adjVertex : entry.getValue()) {
				Vertex sideB = adjVertex.getOtherSide(entry.getKey());
				sb.append(" ").append(sideB.getLabel()).append('[')
						.append(adjVertex.getWeight()).append(']');
			}
		}
		return sb.toString();
	}

	@Override
	public Set<Edge> getEdgesFrom(Vertex vertex) {
		Set<Edge> set = vertexToAdjList.get(vertex);
		return set == null ? Collections.<Edge> emptySet() : set;
	}

	@Override
	public Set<Edge> getEdges() {
		Set<Edge> allEdges = new HashSet<Edge>();
		for (Set<Edge> edges : vertexToAdjList.values()) {
			allEdges.addAll(edges);
		}
		return allEdges;
	}

	@Override
	public Set<Vertex> getVertices() {
		return allVertices;
	}
}
